package event

import java.util.*

object EventFactory {

    private const val KEY_EVENT_BEGIN = "EVENT_BEGIN"
    private const val KEY_EVENT_FINISH = "EVENT_FINISH"
    private const val ERR_NO_MATCH_EVENT = -1

    /**
     * data only contains one event
     */
    private fun List<String>.toEvent(): Event = when {
        this[0].endsWith(EventVMInit.TYPE) -> EventVMInit(this)
        this[0].endsWith((EventObjectAlloc.TYPE)) -> EventObjectAlloc(this)
        this[0].endsWith(EventObjectFree.TYPE) -> EventObjectFree(this)
        this[0].endsWith(EventGCFinish.TYPE) -> EventGCFinish(this)
        this[0].endsWith(EventGCStart.TYPE) -> EventGCStart(this)
        this[0].startsWith(EventMinorGC.TYPE) -> EventMinorGC(this)
        this[0].startsWith(EventFullGC.TYPE) -> EventFullGC(this)
        else -> {
            println("str: ${this[0]}")
            throw EventException(ERR_NO_MATCH_EVENT, "str: ${this[0]}")
        }
    }

    /**
     * data contains some events
     */
    private fun createEventsByLog(data: List<String>): List<Event> {
        val result = LinkedList<Event>()
        var start = 0
        var end = 0
        data.forEachIndexed { index, s ->
            if (s.startsWith(KEY_EVENT_FINISH)) {
                end = index + 1
                result.add(data.subList(start, end).toEvent())
                start = end
            }
        }
        return result
    }

    private fun createEventsByOut(data: List<String>): List<Event> {
        val result = LinkedList<Event>()
        data.forEach {
            if (it.startsWith("Heap")) return result
            result.add(listOf(it).toEvent())
        }
        return result
    }

    fun createEvents(logList: List<String>, outList: List<String>): List<Event> {
        val logEventList = createEventsByLog(logList)
        val outEventList = createEventsByOut(outList)
        val eventList = LinkedList<Event>()

        // Insert EventOutGC object before EventGCFinish
        var i = 0
        logEventList.forEach {
            if (it is EventGCFinish) {
                val eventGC1 = outEventList[i] as EventOutGC
                val eventGC2 = outEventList[i + 1] as EventOutGC
                if (eventGC1 is EventMinorGC && eventGC2 is EventFullGC && eventGC1.gcReason == eventGC2.gcReason) {
                    eventList.add(eventGC1)
                    eventList.add(eventGC2)
                    i += 2
                } else {
                    eventList.add(eventGC1)
                    i++
                }
            }
            eventList.add(it)
        }
        return eventList
    }
}